﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Deployment;
using Microsoft.SharePoint.Publishing;
using System.Xml;
using System.Globalization;
using System.Net;
using System.IO;
using Microsoft.SharePoint.Utilities;
using System.Diagnostics;

namespace Macaw.Moss2007.ContentCopySolution
{
    public class ContentCopyJobDefinition : SPJobDefinition
    {
        public ContentCopyJobDefinition()
            : base()
        {
        }

        public ContentCopyJobDefinition(string jobName, SPService service, SPServer server, SPJobLockType targetType)
            : base(jobName, service, server, targetType)
        {
        }
        public ContentCopyJobDefinition(string jobName, SPWebApplication webApplication)
            : base(jobName, webApplication, null, SPJobLockType.ContentDatabase)
        {
            this.Title = "ContentCopy Deployment";
        }

        private const string QO_RECURSIVE = "<QueryOptions><ViewAttributes Scope=\"Recursive\"/></QueryOptions>";


        private XmlNodeList GetListItems(SPWSLists.Lists ListsWS, string ListName)
        {
            XmlDocument qodoc = new XmlDocument();
            qodoc.LoadXml(QO_RECURSIVE);

            System.Xml.XmlNode result = ListsWS.GetListItems(ListName, null, null, null, int.MaxValue.ToString(), qodoc.DocumentElement, null);
            XmlDocument xDoc = new XmlDocument();
            xDoc.LoadXml(result.OuterXml);
            string ListItemsNamespacePrefix = "z";
            string ListItemsNamespaceURI = "#RowsetSchema";
            string DirectoryNamespacePrefix = "rs";
            string DirectoryNamespaceURI = "urn:schemas-microsoft-com:rowset";
            XmlNamespaceManager NamespaceMngr = new XmlNamespaceManager(xDoc.NameTable);
            NamespaceMngr.AddNamespace(ListItemsNamespacePrefix, ListItemsNamespaceURI);
            NamespaceMngr.AddNamespace(DirectoryNamespacePrefix, DirectoryNamespaceURI);
            XmlNodeList xNList = xDoc.SelectNodes("//rs:data//z:row", NamespaceMngr);
            return xNList;
        }

        public void ContentCopy(SPSite site)
        {
            try
            {
                SPList list = site.RootWeb.Lists["ContentCopy"];
                for (int idx = list.ItemCount - 1; idx >= 0; idx--)
                {
                    string ContentCopy_SourceFullSiteUrl = (string)list.Items[idx]["ContentCopy_SourceFullSiteUrl"];
                    string ContentCopy_SourceUserName = (string)list.Items[idx]["ContentCopy_SourceUserName"];
                    string ContentCopy_SourcePassword = (string)list.Items[idx]["ContentCopy_SourcePassword"];
                    string ContentCopy_SourceDomain = (string)list.Items[idx]["ContentCopy_SourceDomain"];
                    string ContentCopy_SourceListName = (string)list.Items[idx]["ContentCopy_SourceListName"];
                    string ContentCopy_DestSiteUrl = (string)list.Items[idx]["ContentCopy_DestSiteUrl"];
                    ContentCopy(site, ContentCopy_SourceFullSiteUrl, ContentCopy_SourceUserName, ContentCopy_SourcePassword, ContentCopy_SourceDomain, ContentCopy_SourceListName, ContentCopy_DestSiteUrl);
                }
            }
            catch
            {
            }
        }


        public void ContentCopy(SPSite destsite, string ContentCopy_SourceFullSiteUrl, string ContentCopy_SourceUserName, string ContentCopy_SourcePassword, string ContentCopy_SourceDomain, string ContentCopy_SourceListName, string ContentCopy_DestSiteUrl)
        {
            #region Construct SourceCredentials
            ICredentials sourcecredentials = CredentialCache.DefaultCredentials;
            if (!string.IsNullOrEmpty(ContentCopy_SourceUserName))
            {
                if (!string.IsNullOrEmpty(ContentCopy_SourceDomain))
                {
                    sourcecredentials = new NetworkCredential(ContentCopy_SourceUserName, ContentCopy_SourcePassword, ContentCopy_SourceDomain);
                }
                else
                {
                    sourcecredentials = new NetworkCredential(ContentCopy_SourceUserName, ContentCopy_SourcePassword);
                }
            }
            #endregion

            #region Retrieve ListItems from DocumentLibrary
            SPWSLists.Lists sourcews = new SPWSLists.Lists();
            sourcews.Url = SPUrlUtility.CombineUrl(ContentCopy_SourceFullSiteUrl, "/_vti_bin/Lists.asmx");
            sourcews.Credentials = sourcecredentials;
            System.Xml.XmlNodeList sourceitems = GetListItems(sourcews, ContentCopy_SourceListName);
            #endregion

            System.Net.WebClient client = new System.Net.WebClient();
            client.Credentials = sourcecredentials;
            UriBuilder sourceserveruri = new UriBuilder(ContentCopy_SourceFullSiteUrl);
            sourceserveruri.Path = "";
            Uri serveruri = sourceserveruri.Uri;

            SPWeb destweb = destsite.OpenWeb(ContentCopy_DestSiteUrl);
            destweb.AllowUnsafeUpdates = true;
            foreach (XmlNode sourceitem in sourceitems)
            {
                #region Get attributes from source listitem
                string source_ows_FileRef = sourceitem.Attributes["ows_FileRef"].Value;
                DateTime source_ows_Modified = DateTime.Parse(sourceitem.Attributes["ows_Modified"].Value, CultureInfo.InvariantCulture);
                string source_ows__ModerationStatus = sourceitem.Attributes["ows__ModerationStatus"].Value;
                string[] source_orfp = source_ows_FileRef.Split(new string[] { ";#" }, StringSplitOptions.None);
                #endregion

                Uri sourceaddress = new Uri(serveruri, source_orfp[1]);
                Console.WriteLine(sourceaddress.ToString());
                if (source_ows__ModerationStatus == "3")
                {
                    Console.WriteLine("File is unpublished");
                }
                else if (source_ows__ModerationStatus == "0")
                {
                    try
                    {
                        #region Request file from source
                        System.IO.Stream filestream = client.OpenRead(sourceaddress);
                        System.IO.StreamReader filestreamreader = new System.IO.StreamReader(filestream);
                        MemoryStream sourcefiledata = new MemoryStream();
                        StreamWriter sourcefiledatawriter = new StreamWriter(sourcefiledata);
                        sourcefiledatawriter.Write(filestreamreader.ReadToEnd());
                        sourcefiledatawriter.Flush();
                        #endregion

                        SPFile destfile = destweb.GetFile(source_orfp[1]);
                        if (destfile.Exists)
                        {
                            if (destfile.Properties.ContainsKey("sync_ows_Modified"))
                            {
                                DateTime vti_timelastmodified = (DateTime)destfile.Properties["sync_ows_Modified"];
                                if (source_ows_Modified > vti_timelastmodified)
                                {
                                    if (destfile.CheckOutStatus == SPFile.SPCheckOutStatus.None)
                                    {
                                        destfile.CheckOut();
                                    }
                                    #region Update file
                                    SPFile dest = destfile.ParentFolder.Files.Add(source_orfp[1], sourcefiledata, true);
                                    dest.Properties["sync_ows_Modified"] = source_ows_Modified;
                                    dest.Update();
                                    if (dest.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                                    {
                                        dest.CheckIn(string.Empty);
                                    }
                                    if (dest.Item.ParentList.EnableModeration)
                                    {
                                        dest.Approve(string.Empty);
                                    }
                                    dest.Publish(string.Empty);
                                    #endregion
                                }
                            }
                            else
                            {
                                Console.WriteLine("File already exists and is not uploaded via Sync");
                            }
                        }
                        else
                        {
                            #region Create missing folders
                            Stack<string> folderstack = new Stack<string>();
                            SPFolder folder = destfile.ParentFolder;

                            while (!folder.Exists)
                            {
                                folderstack.Push(folder.Name);
                                folder = folder.ParentFolder;
                            }
                            while (folderstack.Count > 0)
                            {
                                folder = folder.SubFolders.Add(folderstack.Pop());
                            }
                            #endregion

                            #region Upload file
                            SPFile dest = destfile.ParentFolder.Files.Add(source_orfp[1], sourcefiledata, false);
                            dest.Properties.Add("sync_ows_Modified", source_ows_Modified);
                            dest.Update();
                            if (dest.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                            {
                                dest.CheckIn(string.Empty);
                            }
                            if (dest.Item.ParentList.EnableModeration)
                            {
                                dest.Approve(string.Empty);
                            }
                            dest.Publish(string.Empty);
                            #endregion
                        }
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("Failed to receive file");
                    }
                }
            }


        }

        public override void Execute(Guid targetInstanceId)
        {
            SPWebApplication webApplication = this.Parent as SPWebApplication;

            for (int idx = webApplication.Sites.Count - 1; idx >= 0; idx--)
            {
                SPSite site = null;
                try
                {
                    site = webApplication.Sites[idx];
                    using (SPSite systemSite = new SPSite(site.ID, site.SystemAccount.UserToken))
                    {
                        ContentCopy(systemSite);
                    }
                }
                catch (Exception ex)
                {
                    SPSecurity.RunWithElevatedPrivileges(delegate
                    {
                        EventLog.WriteEntry(this.GetType().FullName, string.Format("Error occured processing Site Collection \"{0}\":\r\n{1}", site.Url, ex.ToString()), EventLogEntryType.Error);
                    });
                }
            }
        }

    }
}
